using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
#if XRIT_INTEGRATION
using UnityEngine.XR.Interaction.Toolkit;
#endif
namespace BNG {
///
/// Point a line at our GazePointer
///
public class UIPointer : MonoBehaviour {
[Tooltip("The controller side this pointer is on")]
public ControllerHand ControllerSide = ControllerHand.Right;
[Tooltip("If true this object will update the VRUISystem's Left or Right Transform property")]
public bool AutoUpdateUITransforms = true;
public GameObject cursor;
private GameObject _cursor;
[Tooltip("If true the cursor and LineRenderer will be Hidden. Otherwise it will still be show at a fixed length")]
public bool HidePointerIfNoObjectsFound = true;
[Tooltip("How long the line / cursor should extend if no objects are found to point at")]
public float FixedPointerLength = 0.5f;
[Tooltip("If true the cursor object will scale based on how far away the pointer is from the origin. A cursor far away will have a larger cusor than one up close.")]
public bool CursorScaling = true;
[Tooltip("Minimum scale of the Cursor object if CursorScaling is enabled")]
public float CursorMinScale = 0.6f;
public float CursorMaxScale = 6.0f;
private Vector3 _cursorInitialLocalScale;
///
/// 0.5 = Line Goes Half Way. 1 = Line reaches end.
///
[Tooltip("Example : 0.5 = Line Goes Half Way. 1 = Line reaches end.")]
public float LineDistanceModifier = 0.8f;
///
/// Calls Events
///
VRUISystem uiSystem;
PointerEvents selectedPointerEvents;
PointerEventData data;
[Tooltip("LineRenderer to use when showing a valid UI Canvas. Leave null to attempt a GetComponent<> on this object.")]
public LineRenderer lineRenderer;
void Awake() {
if(cursor) {
_cursor = GameObject.Instantiate(cursor);
_cursor.transform.SetParent(transform);
_cursorInitialLocalScale = transform.localScale;
}
// If no Line Renderer was specified in the editor, check this Transform
if (lineRenderer == null) {
lineRenderer = GetComponent();
}
#if XRIT_INTEGRATION
// Setup XRIT info
if (VRUISystem.Instance.UseXRInteractionToolkitUISystem) {
// Add the XRaycaster if it's not currently attached
SetupXRITRaycaster();
}
else {
uiSystem = VRUISystem.Instance;
}
#else
uiSystem = VRUISystem.Instance;
#endif
}
#if XRIT_INTEGRATION
XRRayInteractor xrRay;
XRInteractorLineVisual vis;
public virtual void SetupXRITRaycaster() {
xrRay = gameObject.GetComponent();
if(xrRay == null) {
ActionBasedController abc = gameObject.AddComponent();
xrRay = gameObject.AddComponent();
if (VRUISystem.Instance.UIInputAction != null) {
abc.uiPressAction = new UnityEngine.InputSystem.InputActionProperty(VRUISystem.Instance.UIInputAction);
// For testing
abc.selectAction = new UnityEngine.InputSystem.InputActionProperty(VRUISystem.Instance.UIInputAction);
}
Gradient curColor = null;
LineRenderer lr = GetComponent();
if(lr) {
lr.useWorldSpace = true;
curColor = lr.colorGradient;
}
// Setup XRay Visual
vis = gameObject.AddComponent();
if(curColor != null) {
vis.validColorGradient = curColor;
vis.invalidColorGradient = curColor;
vis.lineWidth = 0.01f;
}
}
}
#endif
void OnEnable() {
#if XRIT_INTEGRATION
if (VRUISystem.Instance.UseXRInteractionToolkitUISystem) {
}
else {
updateUITransforms();
}
#else
updateUITransforms();
#endif
}
void updateUITransforms() {
// Automatically update VR System with our transforms
if (AutoUpdateUITransforms && ControllerSide == ControllerHand.Left) {
uiSystem.LeftPointerTransform = this.transform;
}
else if (AutoUpdateUITransforms && ControllerSide == ControllerHand.Right) {
uiSystem.RightPointerTransform = this.transform;
}
uiSystem.UpdateControllerHand(ControllerSide);
}
public void Update() {
#if XRIT_INTEGRATION
if(VRUISystem.Instance.UseXRInteractionToolkitUISystem) {
// Update raycast line
RaycastHit? hit;
RaycastResult? hitResult;
int hitIndex = -1;
bool hitIsUI = false;
if (xrRay.TryGetCurrentRaycast(out hit, out hitIndex, out hitResult, out hitIndex, out hitIsUI)) {
if((hitResult.HasValue && hitResult.Value.isValid) || (hit.HasValue && hit.Value.collider.gameObject.GetComponent() != null)) {
vis.enabled = true;
}
else {
vis.enabled = false;
}
}
else {
vis.enabled = false;
}
}
else {
UpdatePointer();
}
#else
UpdatePointer();
#endif
}
public virtual void UpdatePointer() {
data = uiSystem.EventData;
// Can bail early if not looking at anything
if (data == null || data.pointerCurrentRaycast.gameObject == null) {
HidePointer();
return;
}
// Set position of the cursor
if (_cursor != null) {
bool lookingAtUI = data.pointerCurrentRaycast.module.GetType() == typeof(GraphicRaycaster);
selectedPointerEvents = data.pointerCurrentRaycast.gameObject.GetComponent();
bool lookingAtPhysicalObject = selectedPointerEvents != null;
// Are we too far away from the Physics object now?
if (lookingAtPhysicalObject) {
if (data.pointerCurrentRaycast.distance > selectedPointerEvents.MaxDistance) {
HidePointer();
return;
}
}
// Can bail immediately if not looking at a UI object or an Object with PointerEvents on it
if (!lookingAtUI && !lookingAtPhysicalObject) {
HidePointer();
return;
}
// Set as local position
float distance = Vector3.Distance(transform.position, data.pointerCurrentRaycast.worldPosition);
_cursor.transform.localPosition = new Vector3(0, 0, distance - 0.0001f);
_cursor.transform.rotation = Quaternion.FromToRotation(Vector3.forward, data.pointerCurrentRaycast.worldNormal);
// Scale cursor based on distance from main camera
float cameraDist = Vector3.Distance(Camera.main.transform.position, _cursor.transform.position);
_cursor.transform.localScale = _cursorInitialLocalScale * Mathf.Clamp(cameraDist, CursorMinScale, CursorMaxScale);
_cursor.SetActive(data.pointerCurrentRaycast.distance > 0);
}
// Update linerenderer
if (lineRenderer) {
lineRenderer.useWorldSpace = false;
lineRenderer.SetPosition(0, Vector3.zero);
lineRenderer.SetPosition(1, new Vector3(0, 0, Vector3.Distance(transform.position, data.pointerCurrentRaycast.worldPosition) * LineDistanceModifier));
lineRenderer.enabled = data.pointerCurrentRaycast.distance > 0;
}
}
public virtual void HidePointer() {
// Hide the line and cursor
if(HidePointerIfNoObjectsFound) {
_cursor.SetActive(false);
lineRenderer.enabled = false;
}
// Show a fixed length line
else {
if (_cursor) {
_cursor.SetActive(false);
}
// Set length to fixed amount
if (lineRenderer) {
lineRenderer.useWorldSpace = false;
lineRenderer.SetPosition(0, Vector3.zero);
lineRenderer.SetPosition(1, new Vector3(0, 0, FixedPointerLength * LineDistanceModifier));
lineRenderer.enabled = true;
}
}
}
}
}